package com.example.service.aboutUser.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.vo.Result;
import com.example.vo.request.*;
import com.example.vo.response.SystemPermissionDTO;
import com.example.vo.response.SystemRoleDTO;
import com.example.vo.response.UserDTO;
import com.example.mapper.aboutUser.RolePermissionRelationMapper;
import com.example.mapper.aboutUser.UserMapper;
import com.example.mapper.aboutUser.UserPermissionRelationMapper;
import com.example.mapper.aboutUser.UserRoleRelationMapper;
import com.example.pojo.aboutUser.*;
import com.example.service.aboutUser.UserPermissionRelationService;
import com.example.service.aboutUser.UserRoleRelationService;
import com.example.service.aboutUser.UserService;
import com.example.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;

import static com.example.utils.SystemConstants.PHONE_ENCRYPT_KEY;
import static com.example.utils.SystemConstants.RESPONSE_GENERAL_ERR;


@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserRoleRelationMapper userRoleRelationMapper;

    @Resource
    private UserRoleRelationService userRoleRelationService;

    @Resource
    private UserPermissionRelationMapper userPermissionRelationMapper;

    @Resource
    private UserPermissionRelationService userPermissionRelationService;

    @Resource
    private RolePermissionRelationMapper rolePermissionRelationMapper;

    /**
     * 用户登录功能
     *
     * @param loginForm 登录表单，包含账号密码
     * @return 是否登录成功
     */
    @Override
    public Result login(LoginForm loginForm, HttpSession session) {
        // 根据账号组装queryWrapper
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getStuId, loginForm.getUsername())
                .eq(User::getIsDelete, false);

        loginForm.setPassword(DigestUtil.md5Hex(loginForm.getPassword()));

        User user = super.getOne(userLambdaQueryWrapper);

        if (user == null) {
            return Result.error("用户未注册");
        }

        if (!loginForm.getPassword().equals(user.getPassword())) {
            return Result.error("密码错误");
        }

        if (!user.getAccState()) {
            return Result.error("当前账号已被禁用，请联系管理员");
        }


        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);

        // 手机号解密
        userDTO.setTelephone(SecureUtil.aes(PHONE_ENCRYPT_KEY.getBytes()).decryptStr(userDTO.getTelephone()));

        // 设置用户角色,及角色的权限
        List<SystemRoleDTO> roleDTOByUserId = this.getRoleDTOByUserId(userDTO.getId());
        userDTO.setRoles(BeanUtil.copyToList(roleDTOByUserId, SystemRoleDTO.class));

        // 设置用户权限
        userDTO.setPermissions(this.userPermissionRelationMapper.getPermissionsByUserId(userDTO.getId()));

        // 用户信息存入session
        session.setAttribute("user" + userDTO.getId(), userDTO);

        // 使用用户的id作为token
        return Result.success(JwtUtil.generateJwt(user.getId().toString()));
    }


    /**
     * 获取当前登录用户的信息
     * 请求在拦截器中已经根据请求头进行处理,获取到userDto对象
     *
     * @return 用户DTO信息
     */
    @Override
    public Result userInfo() {
        return Result.success(UserHolder.getUser());
    }


    /**
     * 用户注册功能
     *
     * @param registerForm 前端传递的注册表单
     * @return 是否注册成功
     */
    @Override
    public Result register(RegisterForm registerForm) {
        // 判断学号/学工号或邮箱是否已经注册过
        User alreadyStuId = super.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getStuId, registerForm.getStuId()));

        if (alreadyStuId != null) {
            return Result.error("此学号/学工号已被注册！", RESPONSE_GENERAL_ERR);
        }

        // RegisterFormDTO拷贝属性到User对象
        User user = BeanUtil.copyProperties(registerForm, User.class);

        // 用户密码进行md5加密
        user.setPassword(DigestUtil.md5Hex(user.getPassword()));

        // 用户手机号进行AES加密
        byte[] encrypt = SecureUtil.aes(PHONE_ENCRYPT_KEY.getBytes()).encrypt(user.getTelephone());
        user.setTelephone(HexUtil.encodeHexStr(encrypt));

        super.save(user);

        return Result.success();
    }

    /**
     * 发送验证码到指定邮箱
     *
     * @param email 邮箱地址
     * @return md5加密过的验证码
     */
    @Override
    public Result getAuthCode(String email) {
        String chars = "0123456789";

        StringBuilder sb = new StringBuilder();
        Random random = new Random();

        for (int i = 0; i < 6; i++) {
            int index = random.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }

        String code = sb.toString();

        // 放入缓存中
        LoginCache.put(email, code);

        // 向邮箱发送验证码
        EmailSender emailSender = new EmailSender(email, code);
        emailSender.send();

        return Result.success();
    }

    /**
     * 忘记密码
     *
     * @param forgetPasswordForm 忘记密码的表单
     * @return 更改新密码是否成功
     */
    @Override
    public Result resetPassword(ForgetPasswordForm forgetPasswordForm) {
        // 校验验证码
        if (!Objects.equals(LoginCache.get(forgetPasswordForm.getEmail()), forgetPasswordForm.getAuthCode())) {
            return Result.error("验证码错误");
        }

        LoginCache.remove(forgetPasswordForm.getEmail());

        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getEmail, forgetPasswordForm.getEmail())
                .set(User::getPassword, DigestUtil.md5Hex(forgetPasswordForm.getPassword()));

        super.update(userLambdaUpdateWrapper);

        return Result.success();
    }

    /**
     * 分页列出用户
     *
     * @param currentPage 当前页
     * @param pageSize    一页中有多少条数
     * @return 用户列表
     */
    @Override
    public Result listUsers(Integer currentPage, Integer pageSize) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getIsDelete, false);

        // db查询
        Page<User> userPage = super.page(new Page<>(currentPage, pageSize), userLambdaQueryWrapper);

        // 组装DTO对象
        Page<UserDTO> userDTOPage = new Page<>();
        BeanUtil.copyProperties(userPage, userDTOPage);

        // User对象转化为DTO对象并存储在Page<UserDTO>对象中
        userDTOPage.setRecords(BeanUtil.copyToList(userPage.getRecords(), UserDTO.class));

        for (UserDTO userDTO : userDTOPage.getRecords()) {
            // 设置用户列表中每一个用户所扮演的角色，及每个角色所拥有的权限
            userDTO.setRoles(this.getRoleDTOByUserId(userDTO.getId()));

            // 设置用户列表中每一个用户所拥有的权限
            userDTO.setPermissions(this.userPermissionRelationMapper.getPermissionsByUserId(userDTO.getId()));

            // 手机号解密
            userDTO.setTelephone(SecureUtil.aes(PHONE_ENCRYPT_KEY.getBytes()).decryptStr(userDTO.getTelephone()));
        }

        return Result.success(userDTOPage);
    }

    /**
     * 修改用户的角色和权限
     *
     * @param updateUserRoleAndPermissionForm 用户新的角色和权限表单
     * @return 是否修改成功
     */
    @Override
    @Transactional
    public Result updateUserRoleAndPermissions(UpdateUserRoleAndPermissionForm updateUserRoleAndPermissionForm) {

        Integer userId = updateUserRoleAndPermissionForm.getUserId();

        if(userId == 3) {
            return Result.error("管理员权限无法修改!");
        }

        // 删除原来的用户角色、权限，参数：用户id
        LambdaQueryWrapper<UserRoleRelation> userRoleRelationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userRoleRelationLambdaQueryWrapper.eq(UserRoleRelation::getUserId, userId);
        userRoleRelationService.remove(userRoleRelationLambdaQueryWrapper);

        LambdaQueryWrapper<UserPermissionRelation> userPermissionRelationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPermissionRelationLambdaQueryWrapper.eq(UserPermissionRelation::getUserId, userId);
        userPermissionRelationService.remove(userPermissionRelationLambdaQueryWrapper);

        // 添加用户新的角色、权限
        List<SystemRoleDTO> roles = updateUserRoleAndPermissionForm.getRoles();
        List<SystemPermissionDTO> permissions = updateUserRoleAndPermissionForm.getPermissions();

        ArrayList<UserRoleRelation> userRoleRelations = new ArrayList<>();
        ArrayList<UserPermissionRelation> userPermissionRelations = new ArrayList<>();

        for (SystemRoleDTO role : roles) {
            UserRoleRelation userRoleRelation = new UserRoleRelation();
            userRoleRelation.setUserId(userId);
            userRoleRelation.setRoleId(role.getId());

            userRoleRelations.add(userRoleRelation);
        }

        for (SystemPermissionDTO permission : permissions) {
            UserPermissionRelation userPermissionRelation = new UserPermissionRelation();
            userPermissionRelation.setUserId(userId);
            userPermissionRelation.setPermissionId(permission.getId());

            userPermissionRelations.add(userPermissionRelation);
        }

        userRoleRelationService.saveBatch(userRoleRelations);
        userPermissionRelationService.saveBatch(userPermissionRelations);

        return Result.success();
    }

    /**
     * 更新用户姓名、手机号
     *
     * @param updateUserInfoForm 用户新的姓名/手机号
     * @return 是否更新成功
     */
    @Override
    public Result updateUserInfo(UpdateUserInfoForm updateUserInfoForm, HttpSession session) {

        // 手机号加密
        byte[] encrypt = SecureUtil.aes(PHONE_ENCRYPT_KEY.getBytes()).encrypt(updateUserInfoForm.getTelephone());
        updateUserInfoForm.setTelephone(HexUtil.encodeHexStr(encrypt));

        // session修改
        Integer userId = updateUserInfoForm.getId();
        UserDTO userDTO = (UserDTO) session.getAttribute("user" + userId);

        // 被修改者是当前登录用户
        if (userDTO != null && Objects.equals(userDTO.getId(), userId)) {
            userDTO.setName(updateUserInfoForm.getName());
            userDTO.setTelephone(updateUserInfoForm.getTelephone());
            session.setAttribute("user" + userDTO.getId(), userDTO);
        }


        // 修改用户数据表
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getId, userId)
                .set(User::getName, updateUserInfoForm.getName())
                .set(User::getTelephone, updateUserInfoForm.getTelephone());

        super.update(userLambdaUpdateWrapper);

        return Result.success();
    }

    /**
     * 检查输入的原始密码是否正确
     *
     * @param passwordOperationForm 输入的原始密码与用户id
     * @return 是否正确
     */
    @Override
    public Result checkPassword(PasswordOperationForm passwordOperationForm) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getId, passwordOperationForm.getId())
                .eq(User::getPassword, DigestUtil.md5Hex(passwordOperationForm.getPassword()));

        User user = super.getOne(userLambdaQueryWrapper);

        if (user != null) {
            return Result.success();
        }

        return Result.error("原密码输入错误，请输入正确密码");
    }

    /**
     * 用户密码的修改
     *
     * @param changePasswordForm 包含用户名id, 新密码password
     * @return 是否修改成功
     */
    @Override
    public Result changePassword(PasswordOperationForm changePasswordForm) {
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();

        userLambdaUpdateWrapper.eq(User::getId, changePasswordForm.getId())
                .set(User::getPassword, DigestUtil.md5Hex(changePasswordForm.getPassword()));

        super.update(userLambdaUpdateWrapper);

        return Result.success();
    }

    /**
     * 禁用指定id的用户账号
     *
     * @param userId 用户id
     * @return 是否禁用成功
     */
    @Override
    public Result forbiddenAccountByUserId(Integer userId) {
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getId, userId)
                .set(User::getAccState, false);

        super.update(userLambdaUpdateWrapper);

        return Result.success();
    }

    /**
     * 启用指定id的用户账号
     *
     * @param userId 用户id
     * @return 是否启用成功
     */
    @Override
    public Result enableAccountByUserId(Integer userId) {
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userLambdaUpdateWrapper.eq(User::getId, userId)
                .set(User::getAccState, true);

        super.update(userLambdaUpdateWrapper);

        return Result.success();
    }

    /**
     * 根据条件查询用户列表
     *
     * @param searchUserConditions 查询条件
     * @return 符合条件的用户
     */
    @Override
    public Result listUsersByConditions(SearchUserConditions searchUserConditions) {

        String name = searchUserConditions.getName();
        String stuId = searchUserConditions.getStuId();
        Integer roleId = searchUserConditions.getRoleId();

        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();

        userLambdaQueryWrapper.eq(!name.equals(""), User::getName, name)
                .eq(!stuId.equals(""), User::getStuId, stuId);

        List<User> users = super.list(userLambdaQueryWrapper);

        List<UserDTO> userDTOS;

        if (roleId != null) {
            List<User> usersByRoleId = this.userRoleRelationMapper.getUsersByRoleId(roleId);

            List<User> intersection = users.stream()
                    .filter(usersByRoleId::contains)
                    .collect(Collectors.toList());

            userDTOS = BeanUtil.copyToList(intersection, UserDTO.class);
        }else {
            userDTOS = BeanUtil.copyToList(users, UserDTO.class);
        }

        // 设置角色和权限
        for (UserDTO userDTO : userDTOS) {
            // 设置用户列表中每一个用户所扮演的角色，及每个角色所拥有的权限
            userDTO.setRoles(this.getRoleDTOByUserId(userDTO.getId()));

            userDTO.setPermissions(this.userPermissionRelationMapper.getPermissionsByUserId(userDTO.getId()));

            // 手机号解密
            userDTO.setTelephone(SecureUtil.aes(PHONE_ENCRYPT_KEY.getBytes()).decryptStr(userDTO.getTelephone()));
        }

        return Result.success(userDTOS);
    }


    // 根据用户id，获取SystemRoleDTO（角色及角色拥有的权限）
    public List<SystemRoleDTO> getRoleDTOByUserId(Integer userId) {

        // 获取用户的角色
        List<SystemRole> roles = this.userRoleRelationMapper.getRolesByUserId(userId);

        List<SystemRoleDTO> roleDTOS = BeanUtil.copyToList(roles, SystemRoleDTO.class);

        // 设置角色的权限
        for (SystemRoleDTO role : roleDTOS) {
            List<SystemPermission> permissions = this.rolePermissionRelationMapper.getPermissionsByRoleId(role.getId());

            role.setSystemPermissions(BeanUtil.copyToList(permissions, SystemPermissionDTO.class));
        }

        return roleDTOS;
    }
}
